/**
 * @file main.cpp
 * @author iwinyeah (iwinyeah@163.com)
 * @brief 用于sammax做的阅读器 主程序
 * @link https://oshwhub.com/sammax/esp32_with_spi_sdcard
 * @version 0.1
 * @date 2023-11-11
 * @copyright Copyright (c) 2023
 *
 */

#include <Hardware.h>
#include <View.h>
#include <RTCPage.h>
#include <RX8025.h>
#include <FS.h>
#include <WiFi.h>

#define uS_TO_S_FACTOR 1000000       // 每秒的微秒数
#define TIME_TO_SLEEP 10             // 视力训练模式下每10秒刷新一次

View *tv;
RTCPage *pCtx;

// 临时保存被选中文件名
char fileName[MAX_NAME_LENGTH];
// 主菜单项
char menuItem[] = {VT_DIR_F, VT_DIR_T, VT_TOMATO, VT_EYE};

void setup()
{
// 串口输出
#ifdef DEBUG_ON
    Serial.begin(115200);
#endif

    int key = getKey();

    // 不是被唤醒的(按EN键复位或重新开机)
    if (esp_sleep_get_wakeup_cause() < ESP_SLEEP_WAKEUP_EXT0)
    {
        if (key == KEY_BK) // 按住上键，再按最下键(EN)复位，用以显示主菜单
        {
            setViewType(VT_MENU);
        }
        else if(key == KEY_MD) // 按住下键，再按最下键(EN)复位，用以快速开始上次阅读或记忆
        {
            int vType = readLastRead(fileName);
            if(vType) // 若读取失败, vType = 0
            {
                setViewType(vType);
            }
        }
        else // 直接按最下键(EN)复位，显示时钟，这就是所谓老板键:)
        {
            setViewType(VT_CLOCK);
        }
        key = KEY_NL; // 吃掉按键(防止触发将要进入的其它视图)
    }
    
    rxTime.begin(); // 初始化时间对象(可能触发网络时间同步)

    int vt = getViewType();
    // VT_EYE,VT_TOMATO,VT_DIR_F,VT_DIR_T,VT_READ,VT_TERM
    if (key == KEY_BK && vt >= VT_EYE && vt <= VT_TERM) // 长按上键返回主菜单
    {
        vt = VT_MENU;
        setViewType(vt);
        // tv = new Menu(); 似乎不需要本行？
        key = KEY_NL; // 吃掉按键(防止触发将要进入的其它视图)
    }

    // 时钟视图
    if (vt == VT_CLOCK)
    {
        tv = new Clock();
        tv->handleKey(key);
    }

    // 主菜单视图
    if (vt == VT_MENU)
    {
        if (key == KEY_BK) // 长按上键关机(白屏，不唤醒)
        {
            vt = VT_NONE;
            setViewType(vt);
            tv = new View();

            wakeUpSeconds = -1; // 不定时唤醒
            wakeUpIOMask = 0; // 不按键唤醒
        }
        else if(key == KEY_MD) // 长按下键，选中菜单项
        {
            vt = menuItem[rtcPgIdx];
            setViewType(vt);
        }
        else // 响应其它按键，并更新菜单显示
        {
            tv = new Menu();
            tv->handleKey(key);
        }
        key = KEY_NL; // 吃掉按键(防止触发将要进入的其它视图)
    }

    // 视力训练视图
    if (vt == VT_EYE)
    {
        tv = new Gaze();
        // 视图训练时，每10秒刷新一次
        wakeUpSeconds = TIME_TO_SLEEP;
    }

    // 番茄钟视图
    if (vt == VT_TOMATO)
    {
        tv = new Tomato();
        tv->handleKey(key);
    }

    // (阅读或记忆)文件选择视图
    if (vt == VT_DIR_F || vt == VT_DIR_T)
    {
        pCtx = new RTCPage(key == KEY_NL);

        switch (key)
        {
        case KEY_MD: // 长按右键，选中当前项
            pCtx->copyFocusString(fileName);
            vt = (vt == VT_DIR_F) ? VT_READ : VT_TERM;
            setViewType(vt);
            key = KEY_NL;

            // 保存阅读类型和文件名，以便需要时快速恢复
            saveLastRead(vt, fileName);
            break;
        case KEY_UP: // 其它方向键上下移动焦点行
        case KEY_DN:
            pCtx->step(key == KEY_DN ? 1 : -1);
        default:
            tv = new PageView(pCtx);
            break;
        }
    }

    // 阅读或记忆视图
    if (vt == VT_READ || vt == VT_TERM)
    {
        if(key == KEY_MD) // 长按下键，在阅读与记忆之间切换
        {
            if(vt == VT_TERM)
            {
                vt = VT_READ;
                key = KEY_NL;
            }
            else
            {
                if(sdMount(true))
                {
                    strcpy(fileName, rtcName);
                    strcpy(fileName + strlen(fileName) - 3, "tmx");

                    if(access(fileName, F_OK) == 0)
                    {
                        vt = VT_TERM;
                        key = KEY_NL;
                    }
                    sdMount(false);
                }
            }

            if(key == KEY_NL)
            {
                strcpy(fileName, rtcName + 4);
                setViewType(vt);
                saveLastRead(vt, fileName);
            }
        }

        pCtx = new RTCPage(key == KEY_NL, fileName);
        if (key == KEY_UP || key == KEY_DN) // 方向键上下换页
        {
            pCtx->step(key == KEY_DN ? 1 : -1);
        }
        tv = new PageView(pCtx);
    }

    if (tv)
    {
        dbLogValue("View: ", getViewType());
        tv->refresh();
    }

    // 检查是否有内容要预读，有则立即进行预读
    // 立即预读的原因是，此时用户刚换页，正在阅读，预读不影响用户体验
    // 若在用户下次换页时再预读，则会有延迟，用户体验不好
    if (pCtx && key != KEY_NL && vt >= VT_DIR_F && vt <= VT_TERM)
    {
        pCtx->checkPreLoad();
    }

    if(wakeUpIOMask) // 若不需按键唤醒，将此值设置为0
    {
        esp_sleep_enable_ext1_wakeup(wakeUpIOMask, ESP_EXT1_WAKEUP_ANY_HIGH);
    }
    if (wakeUpSeconds >= 0) // 若不需定时唤醒，将此值设置为-1
    {
        esp_sleep_enable_timer_wakeup(wakeUpSeconds * uS_TO_S_FACTOR);
    }

    gpioResetPins();
    esp_deep_sleep_start();
/*
   rxTime.begin();
    // delay(1000);
    // syncTime();
   TIME_TM tm;
   tm.tm_year = 2024;
   tm.tm_mon = 3 - 1;
   tm.tm_mday = 28;
   tm.tm_wday = 4;
   tm.tm_hour = 7;
   tm.tm_min = 15;
   tm.tm_sec = 0;
   rxTime.setTime(&tm);
*/
}

void loop()
{
    //delay(1000);
    //syncTime();
    // 在本应用中，运行模式为：唤醒->处理->睡眠，因此不会运行到此处
}
/*
#include <WiFi.h>
#include <HTTPClient.h>
 
const char* ssid = "iwinyeah";
const char* password = "13702290709";
 
void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");
}
 
void loop() {
  HTTPClient http;
 
  // Make a GET request to the specified URL
  http.begin("http://baidu.com");
  int httpCode = http.GET();
 
  // If the response code is 200, proceed
  if (httpCode == 200) {
    // Get the response payload as a string
    String payload = http.getString();
 
    // Use a regular expression to extract the title element
    String title = "";
    if (payload.indexOf("<title>") >= 0 && payload.indexOf("</title>") >= 0) {
      title = payload.substring(payload.indexOf("<title>") + 7, payload.indexOf("</title>"));
    }
 
    Serial.println(title);
  } else {
    Serial.println("Error: " + httpCode);
  }
 
  http.end();
 
  delay(5000); // Wait 5 seconds before requesting again
}*/